---
id: procedure
sidebar_position: 7
hide_table_of_contents: true
title: 存储过程调用
description: dbVisitor 支持存储过程的调用以及传入传出参数，下面是存储过程中一下常见场景。
---
import TOCInline from '@theme/TOCInline';

dbVisitor 支持存储过程的调用以及传入传出参数，下面是存储过程中一下常见场景：

<TOCInline toc={toc} />

## 执行存储过程 {#exec}

有如下：MySQL 存储过程，接受一个参数并产生一个结果集。

```sql title='1. MySQL 存储过程'
create procedure proc_select_table(in userName varchar(200))
begin
    select * from test_user where name = userName;
end;
```

```sql title='2. 编辑查询语句'
➊ {call proc_select_table(?)} ➋ @{resultSet,name=res,javaType=net.demo.UserDTO}
```

- ➊ 调用 proc_select_table 存储过程，并且通过位置参数形式传递入参。
- ➋ 通过 [RESULT 规则](../../rules/assist_rule#result-set) 声明在调用存储过程后，将产生的结果集映射为 UserDTO 对象。

```java title='3. 执行存储过程'
JdbcTemplate jdbc = ...
Map<String, Object> res = jdbc.call(➊ "{call proc_select_table(?)} @{resultSet,name=res,javaType=net.demo.UserDTO}",
                                    ➋ "Kevin");
List<UserDTO> result = (List<UserDTO>)res.get("res"); ➌
```

- ➊ 配置了规则后调用存储过程的最终 SQL 语句。
- ➋ 传递入参
- ➌ 获取 [RESULT 规则](../../rules/assist_rule#result-set) 处理后的结果集，名称为 res。

:::info
如果存储过程有意或者无意产生多个结果集，可以通过使用多个 **[RESULT 规则](../../rules/assist_rule#result-set)** 来分别处理它们。
:::

## 使用名称参数

```java title='执行存储过程'
JdbcTemplate jdbc = ...
Map<String, Object> res = jdbc.call(➊ "{call proc_select_table(:userName)} @{resultSet,name=res,javaType=net.demo.UserDTO}",
                                    ➋ CollectionUtils.asMap("userName", "Kevin"));
List<UserDTO> result = (List<UserDTO>)res.get("res"); ➌
```

➊、➋、➌ 同上，区别为将 `?` 方式传递参数改为使用 `:userName` 方式传参。

:::info
- dbVisitor 支持多种参数传递方式，想要了解更多参数传递内容请到 **[参数传递](../../args/about)** 页面查看。
:::

## 获取 OUT 参数值 {#outp}

```sql title='1. 有如下 MySQL 存储过程'
create procedure proc_select_table(in userName varchar(200), ➊
                                   out outName varchar(200)) ➋ -- 传出参数
begin
    select * from test_user where name = userName; ➌
    set outName = concat(userName,'-str');
end;
```

- ➊ 接收的传入参数。
- ➋ 产生的传出参数。
- ➌ 存储过程执行生成的结果集。

```text title='2. 编辑查询语句'
{
 call proc_select_table(➊ #{userName,jdbcType=varchar},
                        ➋ #{outName, mode=out,jdbcType=varchar})
} ➌ @{resultSet,name=res,javaType=net.demo.UserDTO}
```

```java title='3. 执行存储过程'
JdbcTemplate jdbc = ...
Map<String, Object> args = CollectionUtils.asMap("userName", "Kevin");
Map<String, Object> res = jdbc.call(
                "{call proc_select_table(#{userName,jdbcType=varchar}, " +
                "                        #{outName, mode=out,jdbcType=varchar})" +
                "} @{resultSet,name=res,javaType=net.demo.UserDTO}", 
        CollectionUtils.asMap("userName", "Kevin")
);

String outName = (String)res.get("outName");
List<UserDTO> result = (List<UserDTO>)res.get("res");
```

➊、➋、➌ 同上。

## OUT 参数中使用游标 {#outcur}

```oracle title='1. 有如下 Oracle 存储过程'
create or replace procedure proc_out_cursor(
    ➊ userName in varchar2,
    ➋ tableCursor out sys_refcursor)
as
begin
    open tableCursor for select * from test_user where name = userName;
end;
```

- ➊ 接收的传入参数。
- ➋ 通过 OUT 游标传出结果集。

```text title='2. 编辑查询语句'
{
 call proc_out_cursor(➊ #{userName,jdbcType=varchar},
                      ➋ #{tableCursor,mode=cursor,javaType=net.demo.UserDTO})
}
```

```java title='3. 执行存储过程'
JdbcTemplate jdbc = ...
Map<String, Object> args = CollectionUtils.asMap("userName", "Kevin");
Map<String, Object> res = jdbc.call(
                "{call proc_out_cursor(#{userName,jdbcType=varchar}," +
                "                      #{tableCursor,mode=cursor,javaType=net.demo.UserDTO})" +
                "}", args);

List<UserDTO> result = (List<UserDTO>)res.get("tableCursor");
```

- ➊、➋ 同上。
- 使用游标出参数，只需要在普通 OUT 参数配置中设置 `mode=cursor` 即可。

:::info
有些数据库在使用游标出参时可能需要将 Connection 设置为事务状态下，具体请详细参考应用程序数据库的使用说明。
:::
